/** @file    CommunicationManagerSelector.cpp
 *  @author  Alessandro Polo
 *  @version $Id: CommunicationManagerSelector.cpp 2821 2010-08-01 11:44:26Z alex $
 *  @brief
 * File containing methods for the CommunicationManagerImpl class.
 * The header for this class can be found in CommunicationManagerImpl.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include "CommunicationManagerImpl.h"

 #include <core/Message.h>
 #include <core/Request.h>
 #include <core/UserManager.h>

 #include <stdlib.h>
 #include <time.h>
 #include <algorithm>

using namespace std;
using namespace wosh;

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifdef WOSH_AI

/*
class CommunicatorHolder {

Communicator* comm;

//of current user:
UserInfoCommmunicatorData data; // data.ranking, data.timestamp
URI preferredComm;
URI forcedComm;


}
*/

#endif




////////////////////////////////////////////////////////////////////////////////////// COMMUNICATOR SELECTION

UserInfoCommmunicators* CommunicationManagerImpl::getCommunicatorsOf( const string& username )
 {
	// download user's communicator map, we will update/filter communication in the object untill we have a winner
	// this is a copy! we will free it later
	 UserInfoCommmunicators* usrComms = UserManager::getUserCommunicators( username );
	if ( usrComms == NULL ) { // user not found!??
		Log(LOG_WARNING, ":getCommunicatorsOf() : FAILED getting communicators of user %s [not found!?]", username.c_str() );
		usrComms = new UserInfoCommmunicators();
	 }

	// does the user WANT a custom communicator? (example: 'WOSH' may talk only through SessionManager!)
	string forcedCommURI = UserManager::getUserProperty(username, "CommunicatorForce").toString();
	if ( !forcedCommURI.empty() ) {
		// update ranking of the communicator to the max (won't update timestamp)
		usrComms->updateRankingOf(forcedCommURI, 1.0 );
	 }

	Log(LOG_VERBOSE, ":getCommunicatorsOf() : %d communicators for user %s", usrComms->getCommmunicators().size(), username.c_str() );

	return usrComms;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT CommunicationManagerImpl::intersectCommunicators_( UserInfoCommmunicators* usrComms, const Notification* notification, const std::string& location )
 {
	if ( notification == NULL || usrComms == NULL ) return WRET_ERR_PARAM;

	double minRank = usrComms->getLowestRanking(0.0);

	tStrCommunicatorMap::Iterator it_c;
	const tStrCommunicatorMap::ConstIterator it_c_end = this->comms.end();
	for ( it_c=this->comms.begin(); it_c!=it_c_end; ++it_c ) {
		if ( it_c->second == NULL ) continue;
		if ( !usrComms->getCommmunicators().exists(it_c->first) ) {
			UserInfoCommmunicatorData data;
			data.ranking = 0.0;
			data.timestamp = minRank - 0.01;
			usrComms->getCommmunicators().set(it_c->first, data);
		 }

		// filter type of notification (ex. audio/textual ..)
		if ( ( it_c->second->getChannelTypes() & (int)notification->getNotificationType() ) == false ) {
			usrComms->updateRankingOf(it_c->first, -0.5, true);
			Log(LOG_VERBOSE, ":intersectCommunicators_(%ld) :Skipped %s [%s not supported]", notification->getID(), it_c->first.c_str(), notification->getNotificationTypeAsString() );
		 }

		// increase ranking of communicators near user's location
		if ( !location.empty() && it_c->second->isInLocation(location) ) {
			usrComms->updateRankingOf(it_c->first, 0.5, true);
		 }
	 }
	
	// update timestamp of TIMEOUT communicators
	UserInfoCommmunicators::tStrUserInfoCommmunicatorDataMap::Iterator it_uic;
	const UserInfoCommmunicators::tStrUserInfoCommmunicatorDataMap::ConstIterator it_uic_end = usrComms->getCommmunicators().end();
	for ( it_uic=usrComms->getCommmunicators().begin(); it_uic!=it_uic_end; ++it_uic ) {
		if ( this->comms.exists(it_uic->first) ) continue;
// bug.. decrease ranking of
it_uic->second.ranking = minRank - 0.02;
		it_uic->second.timestamp = -it_uic->second.timestamp;
	 }

	return WRET_OK;
 }

////////////////////////////////////////////////////////////////////////////////////// COMMUNICATOR SELECTION
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////// INTERPRETER SELECTION

bool CommunicationManagerImpl::isInterpreterRequired_( const Notification* notification, const wosh::Communicator* comm )
 {
	if ( notification == NULL || comm == NULL ) return false;

	if ( notification->getProtocol() != Notification::PROTOCOL_UNKNOWN &&
		comm->getProtocol() != Notification::PROTOCOL_UNKNOWN &&
		(comm->getProtocol() & notification->getProtocol()) == false ) {
		Log(LOG_VERBOSE, ":isInterpreterRequired_(%ld) : PROTO Translation[%s=>%s] for [%s] [%s=>%s]", notification->getID(),
						notification->getProtocolAsString(), (int)comm->getProtocolAsString(),
						comm->getURI().toString().c_str(), notification->getSender_User().c_str(), notification->getRecipent_User().c_str() );
		return true;
	 }

	if ( (comm->getChannelTypes() & (int)notification->getNotificationType()) == false ) {
		Log(LOG_VERBOSE, ":isInterpreterRequired_(%ld) : TYPE Translation[%s=>(%d)] for [%s] [%s=>%s]", notification->getID(),
						notification->getNotificationTypeAsString(), comm->getChannelTypesAsString().c_str(),
						comm->getURI().toString().c_str(), notification->getSender_User().c_str(), notification->getRecipent_User().c_str() );
		return true;
	 }

	return false;
}

wosh::Interpreter* CommunicationManagerImpl::selectInterpreter_( const Notification* notification, const wosh::Communicator* comm )
 {
	wosh::Interpreter* inter = NULL;

	tStrInterpreterMap::ConstIterator it_i;
	const tStrInterpreterMap::ConstIterator it_i_end = this->transl.end();

	if ( (comm->getChannelTypes() & (int)notification->getNotificationType()) == false ) {
		for ( it_i=this->transl.begin(); it_i!=it_i_end; ++it_i ) {
			if ( it_i->second == NULL ) continue;
			if ( (it_i->second->getInputTypes() & (int)notification->getNotificationType()) == false ) continue;
			if ( (it_i->second->getOutputTypes() & comm->getChannelTypes()) == false ) continue;
			inter = it_i->second;
			break;
		 }
	 }

	if ( comm->getProtocol() != notification->getProtocol() ) {
		for ( it_i=this->transl.begin(); it_i!=it_i_end; ++it_i ) {
			if ( it_i->second == NULL ) continue;
			if ( (it_i->second->getInputProtocols() & (int)notification->getProtocol()) == false ) continue;
			if ( (it_i->second->getOutputProtocols() & (int)comm->getProtocol()) == false ) continue;
			inter = it_i->second;
			break;
		 }
	 }
	if ( inter == NULL ) {
		Log(LOG_VERBOSE, ":selectInterpreter_(%ld) : FAILED PROTO [%s=>%s] for [%s] [%s=>%s]", notification->getID(),
						notification->getProtocolAsString(), (int)comm->getProtocolAsString(),
						comm->getURI().toString().c_str(), notification->getSender_User().c_str(), notification->getRecipent_User().c_str() );
		return NULL;
	 }

	return inter;
 }

/////////////////////////////////////////////////////////////////////////////////////// INTERPRETER SELECTION



//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
